﻿/**
*
* 6dn RichTextArea 

*----------------------------------------------------------------
* @notice 6dn RichTextArea
* @author 6dn
* @as version1.0
* @date 2010-3-29
*
* AUTHOR ******************************************************************************
* 
* authorName : 黎新苑 - www.6dn.cn
* QQ :160379558(6dnの星)
* MSN :xdngo@hotmail.com
* email :6dn@6dn.cn, xdngo@163.com
* webpage : http://www.6dn.cn
* 
* LICENSE ******************************************************************************
* 
* ① RichTextArea类基于FP9,as3.0的轻量级富文本类!(聊天类型,只支持单行图文混排);
* ② 支持外部图片,可使用jpg,png静态图片,也可以使用gif动态图片,当然,也可以使用有linkage库链接的MovieClip；
* ③ 轻量级,使用简便,使用xml配置,通用的字符表情,输入后马上显示;
* ④ 可使用htmlText,可扩展加入超链接,文字粗细,下划线等textFormat(注:由于styleSheet 和 textFormat冲突,所以这里不能使用styleSheet);
* ⑤ 通过richText属性set 和 get,方便易用;(richText值取到的字符串被Player自动加入了一些html标签,但不影响正常使用);
* ⑥ 此类作为开源使用，但请重视作者劳动成果，请使用此类的朋友保留作者信息。
* Please, keep this header and the list of all authors
* 
* ******************************************************************************
*   构造方法:
		RichTextArea($w:int, $h:int) //创建一个宽为$w,高为$h的RichTextArea

	公共方法:
		-appendRichText($str:Stirng):void     //追加$str到RichTextArea;
		-insertRichText($str:String, $beginIndex:int=-1, $endIndex:int = -1):void    //在$beginIndex和$endIndex之间插入字符，默认位置为文本caretIndex即，当前光标位置
		-clear():void                         //清空RichTextArea并回到初始状态
		-resizeTo($w:int, $h:int):void  	  //重新设定RichTextArea的width,height
		-autoAdjust():void  				  //当外部动态设定了textField的x,y或width,height, 可以使用该方法自动校正使RichTextArea显示正常


	公共属性:
		-textField:TextField  [read-only]   //取得RichTextArea中的文本对象
		-richText:String   [read-write]  //设置和读取富文本的值
		-configXML:XML  [read-write]   //富文本对应的XML配置,请参照示例中的格式
			//iconUrl:String 可以是外部链接，也可以是有linkage的库链接
			//iconType:String [movieClip | jpg | gif] 三种格式对应
			//iconStr:String 配置替换的字符串
			
		-defaultTextFormat:TextFormat [read-write]  //设置和读取富文本的defaultTextFormat; (当RichTextArea已经ADDED_TO_STAGE在舞台显示列表中时,如要动态改变textField的defaultTextFormat请用该属性)

	用法示例 usage：
		var _richTextArea:RichTextArea = new RichTextArea(550,400);
		_richTextArea.configXML = <root>
									<icon iconUrl='myMC' iconType ="movieClip" iconStr=":]"/>
									<icon iconUrl='img/1.jpg' iconType ="jpg" iconStr=":o"/>
									<icon iconUrl='img/2.gif' iconType ="jpg" iconStr=":)"/>
								</root>;
		_richTextArea.x = 0;
		_richTextArea.y = 0;
		_richTextArea.textField.wordWrap=true;
		_richTextArea.textField.multiline=true;
		_richTextArea.textField.border = true;
		_richTextArea.textField.type = TextFieldType.INPUT;
		
		_richTextArea.richText = "Hi!:] welcome to <b><font color='#0033FF' size='13'><a href=\"http://www.6dn.cn/blog\" >6DN Blog</a></font></b>! :) ";
		
		addChild(_richTextArea);
		
		trace(_richTextArea.richText);

* 
* 
*/

package com._public._text
{
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TextEvent;
	import flash.events.IOErrorEvent;
	import flash.events.KeyboardEvent;
	
	import flash.text.TextLineMetrics;
	import flash.text.TextField;
	import flash.text.TextFormat;
	
	import flash.utils.ByteArray;
	import flash.utils.setTimeout;
	import flash.utils.getDefinitionByName;

	import flash.geom.Rectangle;
	import flash.net.URLRequest;	
	
	public class RichTextArea extends Sprite
	{
		private var _textField:TextField;
		private var _cacheTextField:TextField;
		private var _txtInfo:Object;
		private var _configXML:XML;
		private var _richTxt:String;	
		private var _defaultFormat:TextFormat;
		
		private var _spriteMap:Array;
		private var _spriteContainer:Sprite;
		private var _spriteMask:Sprite;
		private var _this:Sprite;
		private var _rectangle:Rectangle;
		private var _gifPlayer:GifPlayer;
		
		private const PLACEHOLDER:String = "　";
		private const TYPE_MOVIECLIP:String = "movieClip";
		private const TYPE_JPG:String = "jpg";
		private const TYPE_GIF:String = "gif";
		private const STATUS_INIT:String = "init";
		private const STATUS_LOADED:String = "loaded";
		private const STATUS_NORMAL:String = "normal";
		private const STATUS_CHANGE:String = "change";
		private const STATUS_CONVERT:String = "convert";
		private const STATUS_SCROLL:String = "scroll";
		private const STATUS_INPUT:String = "input";
		
		public function RichTextArea($w:int, $h:int) 
		{
			_this = this;
			
			_spriteMap = new Array();
			_configXML = new XML();
			
			_txtInfo = {
				cursorIndex:null,
				firstPartLength:null,
				lastPartLength:null
			}
			
			//
			_gifPlayer = new GifPlayer();
			
			_cacheTextField = new TextField();
			_cacheTextField.multiline = true;
			
			_textField = new TextField();
			_textField.width = $w;
			_textField.height = $h;
			
			_defaultFormat = new TextFormat();
			_defaultFormat.size = 12;
			_defaultFormat.letterSpacing = 0;
			
			
			
			_spriteContainer = new Sprite();
			_spriteMask = new Sprite();
			drawRectGraphics(_spriteMask,$w,$h, true);
			_spriteContainer.mask = _spriteMask;
			
			_this.addChild(_textField);
			_this.addChild(_spriteContainer);
			_this.addChild(_spriteMask);
			
			//
			_this.addEventListener(Event.ADDED_TO_STAGE, initHandler);
			_this.addEventListener(Event.UNLOAD, unloadHandler);
			
			_textField.addEventListener(MouseEvent.CLICK, clickHandler);
			_textField.addEventListener(KeyboardEvent.KEY_DOWN, keyHandler);

			_textField.addEventListener(Event.CHANGE , changeHandler);
			_textField.addEventListener(Event.SCROLL , scrollHandler);
			
		}
		
		public function appendRichText($str:String):void
		{
			var $_htmlText:String = _textField.htmlText;
			$_htmlText += $str;
			_textField.htmlText = $_htmlText;
			convert();
		}
		
		public function insertRichText($str:String, $beginIndex:int=-1, $endIndex:int = -1):void
		{
			$beginIndex = $beginIndex != -1 ? $beginIndex : _textField.selectionBeginIndex;
			$endIndex = $endIndex != -1 ? $endIndex : _textField.selectionEndIndex;
			_textField.replaceText($beginIndex, $endIndex, $str);
			refreshArr($beginIndex, $str.length- ($endIndex - $beginIndex));
			
			//setTextInfo();
			
			controlManager(STATUS_CHANGE);
			stage.focus = _textField;
		}
		public function resizeTo($w:int, $h:int):void
		{
			_textField.width = $w;
			_textField.height = $h;
			_spriteContainer.x = _textField.x;
			_spriteContainer.y = _textField.y;
			drawRectGraphics(_spriteMask, $w, $h,true);
		}
		
		public function autoAdjust():void
		{
			_spriteContainer.x = _textField.x;
			_spriteContainer.y = _textField.y;
			drawRectGraphics(_spriteMask,_textField.width, _textField.height,true);
		}
		
		public function clear():void
		{
			_spriteMap = [];
			
			_txtInfo = {
				cursorIndex:null,
				firstPartLength:null,
				lastPartLength:null
			}
			
			while (_spriteContainer.numChildren) {
				_spriteContainer.removeChildAt(0);
			}
			_textField.htmlText = "";
		}
		//============================================================================
		// Event Handler
		//============================================================================	
		private function initHandler(evt:Event):void
		{
			_defaultFormat = _textField.defaultTextFormat; 
		}
		private function unloadHandler(evt:Event):void
		{
			_textField.removeEventListener(MouseEvent.CLICK, clickHandler);
			_textField.removeEventListener(KeyboardEvent.KEY_DOWN, keyHandler);
			_textField.removeEventListener(Event.CHANGE, changeHandler);
			_textField.removeEventListener(Event.SCROLL , scrollHandler);
			
			_this.removeChild(_textField);
			_this.removeChild(_spriteContainer);
			_this.removeChild(_spriteMask);
		}
		
		private function clickHandler(evt:Event):void
		{
			setTextInfo();
		}
		
		private function keyHandler(evt:KeyboardEvent):void
		{
			setDefaultFormat();
			setTextInfo();
			//trace("keydown");
		}	
		
		private function scrollHandler(evt:Event):void
		{
			
			if(_textField.htmlText ==null || _textField.htmlText =="") return;
			controlManager(STATUS_SCROLL);
		}
		private function changeHandler(evt:Event):void
		{
			controlManager(STATUS_CHANGE);	
		}
		//============================================================================
		// control function
		//============================================================================		
		private function controlManager($eventStr:String):void
		{
			if ($eventStr == STATUS_CONVERT) {
				convert();
			}else if ($eventStr == STATUS_CHANGE) {	
				setDefaultFormat();
				change();
				refresh();
				convert();
				setTextInfo();
			}else if ($eventStr == STATUS_SCROLL) {
				refresh();
			}
			
		}
		
		//============================================================================
		// convert & revert
		//============================================================================	
		
		private function convert():void
		{
			var $_replaceStr:String = PLACEHOLDER;
			var $_strLen:int;
			var $_id:int;
			var $_index:int;
			var $_iconStr:String ;
			var $_iconInfo:Object;
			var $_item:DisplayObject;
			
			while ($_index != -1) {	
				
				$_iconInfo = getInfoFormXML(_textField.text);
				$_index = $_iconInfo.index;
				
				if ($_index != -1) {
					refreshArr($_index, $_replaceStr.length - $_iconInfo.iconStr.length);
					
					$_strLen = $_iconInfo.iconStr.length;
					_textField.replaceText($_iconInfo.index, $_iconInfo.index + $_strLen, $_replaceStr);
					
					$_id = _spriteMap.length;
					
					if ($_iconInfo.iconType == TYPE_MOVIECLIP) {
						$_item = addMovieClip($_iconInfo, $_id, onItemLoaded); 
					}else if ($_iconInfo.iconType == TYPE_JPG) { 
						$_item = addJpg($_iconInfo, $_id, onItemLoaded);
					}else if ($_iconInfo.iconType == TYPE_GIF) { 
						$_item = addGif($_iconInfo, $_id, onItemLoaded);
					}
					
					_spriteMap.push( { 
						item: $_item,
						iconStr : $_iconInfo.iconStr,
						type : $_iconInfo.iconType,
						url : $_iconInfo.iconUrl, 
						index : $_index,
						textFormat : null,
						status: STATUS_INIT
					});
					
					if($_item) _spriteContainer.addChild($_item);
					
				}
			}
			//trace(_textField.caretIndex);
		}
		
		private function revert():String
		{
			var $_replaceStr :String= PLACEHOLDER;
			var $_placeHolderLen:int = $_replaceStr.length;
			
			var $_arr:Array = _spriteMap;
			var $_len:int = _spriteMap.length;
			
			var $_index:int;
			
			var $_info:Object;
			var $_item:Sprite;
			
			var $_returnStr:String ="";
			
			_cacheTextField.htmlText = _textField.htmlText;
			
			$_arr.sortOn(["index"], 16);
			
			while ($_len--) {
				$_info  = $_arr[$_len];
				if($_info){
					$_index = $_info.index;
					_cacheTextField.replaceText($_index, $_index + $_placeHolderLen, $_info.iconStr);
				}
			}
			
			$_returnStr = _cacheTextField.htmlText;
			return $_returnStr;
		}
		
		//-------------------------------------------------------------------------
		// refresh
		//-------------------------------------------------------------------------
		
		private function refreshArr($index:int,$num:int):void
		{
			var $_arr:Array = _spriteMap;
			var $_len:int = $_arr.length;
			var $_info:Object;
			for (var i:int = 0; i < $_len; i++) {
				$_info = $_arr[i];
				if ($_info) {
					if ($_info.index >= $index) {
						$_info.index += $num;
					}
				}
			}
			if ($num != 0) {
				_textField.setSelection(_textField.caretIndex + $num, _textField.caretIndex + $num);
				setTextInfo();
			}
		}
		
		private function change():void
		{
			var $_arr:Array = _spriteMap;
			var $_len:int = $_arr.length;
			
			var $_info:Object;
			var $_item:DisplayObject;
			
			var $_textFormat:TextFormat;
			var $_textInfo:Object = getTextInfo();
			var $_cursorIndex:int = $_textInfo.cursorIndex < _txtInfo.cursorIndex ? $_textInfo.cursorIndex : _txtInfo.cursorIndex;
			var $_gap:int = $_textInfo.firstPartLength - _txtInfo.firstPartLength + $_textInfo.lastPartLength - _txtInfo.lastPartLength;
			//trace($_gap, $_textInfo.cursorIndex , _txtInfo.cursorIndex);
			while ($_len--) 
			{
				$_info = $_arr[$_len];
				if($_info){
					$_item = $_info.item;
					if ($_info.index >= $_cursorIndex) $_info.index += $_gap;
					if ( $_info.index < 0 || $_info.index >= _textField.length) {
						_spriteContainer.removeChild($_item);
						$_arr[$_len] = null;
						$_info = null;
						//trace("remove1");
					}else{
						$_textFormat = _textField.getTextFormat($_info.index);
						
						if ($_info.status == STATUS_NORMAL && $_textFormat.font != $_info.textFormat.font) {	
							//trace($_info.index , $_cursorIndex);
							_spriteContainer.removeChild($_item);
							$_arr[$_len] = null;
							$_info = null;
							//trace("remove2");
						}
					}
				}
			}
			
		}
		
		private function refresh():void
		{
			var $_arr:Array = _spriteMap;
			var $_len:int = $_arr.length;
			
			var $_info:Object;
			var $_item:DisplayObject;
			var $_rect:Rectangle;
			
			var $_txtLineMetrics :TextLineMetrics ;
			var $_lineHeight:int;
			
			while ($_len--) 
			{
				$_info = $_arr[$_len];
				if($_info){
					$_item = $_info.item;
					
					$_rect = _textField.getCharBoundaries($_info.index);
					if ($_rect) {
						$_txtLineMetrics = _textField.getLineMetrics(_textField.getLineIndexOfChar($_info.index));
						$_lineHeight = $_rect.height *0.5 > $_item.height? $_txtLineMetrics.ascent- $_item.height : ($_rect.height - $_item.height)*0.5;// $_txtLineMetrics.ascent ;// + $_txtLineMetrics.descent * 0.5;
						$_item.visible = true;
						$_item.x =  $_rect.x + ($_rect.width - $_item.width)*0.5;
						$_item.y =  $_rect.y + $_lineHeight;
					}else {
						$_item.visible = false;
					}
				}
			}
			
			setContainerPos();
			
		}
		private function onItemLoaded($id:int):void
		{
			setFormat($id);
			refresh();
		}
		private function setFormat($id:int):void
		{
			var $_format:TextFormat;
			var $_item:DisplayObject;
			var $_rec:Rectangle;
			var $_info:Object = _spriteMap[$id];
			
			$_item = $_info.item;
			$_format = new TextFormat();
			$_format.size = $_item.height;
			$_format.font = $_item.name;
			$_format.letterSpacing = $_item.width - $_item.height * 0.5;
			_textField.setTextFormat($_format, $_info.index);
			
			$_info.textFormat = $_format;
			$_info.status = STATUS_NORMAL;
		}
		private function setDefaultFormat():void
		{
			_textField.defaultTextFormat = _defaultFormat;
		}
		
		private function setTextInfo():void
		{
			_txtInfo = {
				cursorIndex : _textField.caretIndex,
				firstPartLength : _textField.caretIndex,
				lastPartLength : _textField.length - _textField.caretIndex
			}
		}
		private function getTextInfo():Object
		{
			var $_obj:Object = {
				cursorIndex : _textField.caretIndex,
				firstPartLength : _textField.caretIndex,
				lastPartLength : _textField.length - _textField.caretIndex
			};
			return  $_obj;
		}
		//-------------------------------------------------------------------------
		// position
		//-------------------------------------------------------------------------

		private function getTextFieldPos():Object
		{
			var $_xpos:Number = _textField.scrollH;
			var $_n:int = _textField.scrollV-1;
			var $_ypos:Number = 0;
			while ($_n--) {
				$_ypos += _textField.getLineMetrics($_n).height;
			}
			return { x:-$_xpos, y:-$_ypos };
		}
		
		private function setContainerPos():void
		{
			var $_txtPos:Object = getTextFieldPos();
			_spriteContainer.x = _textField.x + $_txtPos.x;
			_spriteContainer.y = _textField.y + $_txtPos.y;
		}
		//-------------------------------------------------------------------------
		// configXML
		//-------------------------------------------------------------------------
		private function getInfoFormXML($str:String):Object
		{
			var $_xml:XML = _configXML;
			var $_len:int = $_xml.icon.length();
			var $_index:int = -1;
			var $_id:int = -1;
			if ($_len <= 0) return null;
			
			var $_info:Object = {
				index: -1,
				iconStr:"",
				iconUrl:"",
				iconType:""
			}
			
			for (var i:int = 0; i < $_len; i++ ) {
				
				$_index = $str.indexOf(getIconStr(i));
				
				if ($_id == -1 || ($_index != -1 && $_id > $_index)) {
					$_id = $_index;
					$_info.index = $_index;
					$_info.iconStr = getIconStr(i);
					$_info.iconUrl = getIconUrl(i);
					$_info.iconType = getIconType(i);
				}
			}
			return $_info; 
		}
		
		private function getIconStr($index:int):String
		{
			var $_xml:XML = _configXML;
			return $_xml.icon[$index].@iconStr;
		}
		
		private function getIconUrl($index:int):String
		{
			var $_xml:XML = _configXML;
			return $_xml.icon[$index].@iconUrl;
		}
		
		private function getIconType($index:int):String
		{
			var $_xml:XML = _configXML;
			return $_xml.icon[$index].@iconType;
		}
		
		//-------------------------------------------------------------------------
		// addMovieClip  
		//-------------------------------------------------------------------------
		private function addMovieClip($info:Object, $id:int = 0, $onComplete:Function = null):DisplayObject
		{
			var $_class:Object = getDefinitionByName($info.iconUrl) as Object;
			var $_item:DisplayObject = new $_class();
			var $_eventHandler:Function = function(evt:Event):void {
				if($onComplete !=null ) $onComplete($id);
			}
			$_item.addEventListener(Event.ADDED_TO_STAGE, $_eventHandler);
			
			return $_item;
		}
		
		private function addJpg($info:Object, $id:int = 0, $onComplete:Function = null):DisplayObject
		{
			var $_sprite:Sprite = new Sprite();
			var $_imgLoader:Loader = new Loader();
			var $_info:Object = _spriteMap[$id];
			var $_onComplete:Function = function(evt:Event):void
			{
				if ($onComplete != null ) $onComplete($id); 
			}
			var $_onError:Function = function(evt:Event):void
			{
				drawErrGraphics($_sprite); 
				if($onComplete !=null ) $onComplete($id);
			}
			$_imgLoader.load(new URLRequest($info.iconUrl));
			$_imgLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, $_onComplete);
			$_imgLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, $_onError );
			$_sprite.addChild($_imgLoader);
			
			return $_sprite;
		}
		private function addGif($info:Object, $id:int = 0, $onComplete:Function = null):DisplayObject
		{
			var $_sprite:Sprite = new Sprite();
			var $_info:Object = _spriteMap[$id];
			var $_onComplete:Function = function(evt:Object):void
			{
				var $_frameRect:Rectangle = evt.rect;
				drawRectGraphics($_sprite, $_frameRect.width, $_frameRect.height, false, 0);
				if ($onComplete != null ) $onComplete($id); 
			}
			var $_onError:Function = function(evt:Event):void
			{
				drawErrGraphics($_sprite); 
				if($onComplete !=null ) $onComplete($id);
			}
			_gifPlayer.createGif($_sprite, $info.iconUrl, $_onComplete, $_onError);
			return $_sprite;
		}
		//-------------------------------------------------------------------------
		// draw graphics
		//-------------------------------------------------------------------------

		private function drawErrGraphics($container:Sprite):void 
		{
			$container.graphics.clear();
			$container.graphics.lineStyle(1,0xff0000);
			$container.graphics.beginFill(0xffffff);
			$container.graphics.drawRect(0, 0, 10, 10);
			
			$container.graphics.moveTo(0, 0);
			$container.graphics.lineTo(10, 10);
			$container.graphics.moveTo(0, 10);
			$container.graphics.lineTo(10, 0);
			
			$container.graphics.endFill();
		}
		
		private function drawRectGraphics($container:Sprite, $w:int = 10, $h:int = 10, $isClear:Boolean = false,  $alpha:int = 1):void 
		{
			if($isClear) $container.graphics.clear();
			$container.graphics.beginFill(0x0,$alpha);
			$container.graphics.drawRect(0, 0, $w, $h);
			$container.graphics.endFill();
		}
		//============================================================================
		//setter & getter
		//============================================================================
		public function get textField():TextField
		{
			return _textField;
		}
		public function set richText($str:String):void
		{
			clear();
			_richTxt = $str;
			_textField.htmlText = $str;	
			
			if ($str == null || $str == "" || _configXML == null ) return;
			
			controlManager(STATUS_CONVERT);
		}
		public function get richText():String
		{
			return revert();
		}
		
		public function set configXML($xml:XML):void
		{
			_configXML = $xml;
		}
		public function get configXML():XML
		{
			return _configXML;
		}
		public function set defaultTextFormat($textFormat:TextFormat):void
		{
			_defaultFormat = $textFormat;
		}
	}
	
}


//*****************************************************
// 以下为包外类-----------
//*****************************************************

//==============================================================================================
// gifPlayer
// 注:gifPlayer作者是bytearray.org ,此处为引用,如需源码,请查看http://code.google.com/p/as3gif/;
//==============================================================================================

import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.net.URLRequest;
import flash.utils.ByteArray;

internal class GifPlayer {
	private var _gifPlayerClass:Object;
	private var _gifPlayerEvent:Object;
	private var _byteArray:ByteArray;
	private var _loader:Loader;
	
	private	const GIFPLAYERSTR:String ="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"
	
	public function GifPlayer()
	{
		_byteArray = new Base64(GIFPLAYERSTR);
		_loader = new Loader();
		createClass();
	}
	public function createGif($container:Sprite, $url:String, $onComplete:Function = null, $onError:Function = null):void
	{
		var $_gifPlayer:*;
		var $_fun:Function = function():void
		{
			$_gifPlayer = new _gifPlayerClass();
			$_gifPlayer.load( new URLRequest ($url) );	
			$_gifPlayer.addEventListener( _gifPlayerEvent.COMPLETE, $onComplete);
			$_gifPlayer.addEventListener(IOErrorEvent.IO_ERROR, $onError );
			$container.addChild($_gifPlayer);
		}
		createClass($_fun);
	}
	
	private function createClass($onComplete:Function = null):void
	{
		if (_gifPlayerClass != null ) {
			if ($onComplete != null) $onComplete();
			return;
		}
		
		var $_completeHandler:Function = function(evt:Event):void {
			_gifPlayerClass = _loader.contentLoaderInfo.applicationDomain.getDefinition("org.bytearray.gif.player.GIFPlayer") as Object;
			_gifPlayerEvent = _loader.contentLoaderInfo.applicationDomain.getDefinition("org.bytearray.gif.events.GIFPlayerEvent") as Object;
			if ($onComplete != null) $onComplete();
		}
		_loader.loadBytes(_byteArray);
		_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, $_completeHandler);
	}
	public function get gifPlayerClass ():Object
	{
		return _gifPlayerClass;
	}
	public function get gifPlayerEvent ():Object
	{
		return _gifPlayerEvent;
	}
}

//============================================================================
// Base64 
// 通用的base64转byteArray;
//============================================================================

internal class Base64 extends ByteArray {
	private static  const BASE64:Array = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,0,0,0,63,52,53,54,55,56,57,58,59,60,61,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,0,0,0,0,0];
	public function Base64(str:String):void {
		var n:int, j:int;
		for (var i:int = 0; i < str.length && str.charAt(i) != "="; i++) {
			j = (j << 6) | BASE64[str.charCodeAt(i)];
			n += 6;
			while (n >= 8) {
				writeByte((j >> (n -= 8)) & 0xFF);
			}
		}
		position = 0;
	}
}